/****************************************************************************
*
*					Functions of class PGMStruct
*
****************************************************************************/

#include <string>
#include <iostream>
#include <fstream>
#include <vector>

#include "PGMStruct.h"


/****************************************************************************
*					PGMStruct::PGMStruct
*
*	Description:	Standard constructor with initialization of internal variables
*	Parameters:		-
*	Returns:		-
*
****************************************************************************/
PGMStruct::PGMStruct (void) {
	this->clear();
}


/****************************************************************************
*					PGMStruct::~PGMStruct
*
*	Description:	Standard destructor with removing all dynamic internal variables
*	Parameters:		-
*	Returns:		-
*
****************************************************************************/
PGMStruct::~PGMStruct (void) {
	for (unsigned int i = 0; i < featureTypes.size(); i++)
		if (featureTypes[i] != NULL)
			delete featureTypes[i];
}


/****************************************************************************
*					PGMStruct::initStructure
*
*	Description:	Initialize internal variables by observing data set parameters
*	Parameters:		dataSet - data set to initilize internal variables from data
*	Returns:		0
*
****************************************************************************/
int PGMStruct::initStructure (DataSet& dataSet) {
	this->clear();
	nVars = dataSet.maxValues.size();
	variables.resize (nVars,0);
	numOfValues.resize(nVars);
	for (unsigned int i = 0; i < nVars; i++) {
		numOfValues[i] = dataSet.maxValues[i] + 1;
	}
	return 0;
}


/****************************************************************************
*					PGMStruct::addFeature
*
*	Description:	Adds a features with a given id and specified varIDs and their values
*					Utilizes or deletes content of newFeature
*	Parameters:		domain - a set of variable IDs that are utilized by the feature
*					newFeature - a pointer to a new feature (it is utilized of deleted)
*	Returns:		0  - if fearture was added to PGM
*					-1 - if fearture already existed in PGM
*
****************************************************************************/
int PGMStruct::addFeature (std::vector <unsigned int>& domain, Feature* newFeature) {
	return addFeature (domain, newFeature, 0.0);
}


/****************************************************************************
*					PGMStruct::addFeature
*
*	Description:	Adds a features with a given id and specified varIDs and their values
*					Utilizes or deletes content of newFeature
*	Parameters:		domain - a set of variable IDs that are utilized by the feature
*					newFeature - a pointer to a new feature (it is utilized of deleted)
*					newWeight - an approximate suggestion for the new weight
*	Returns:		0  - if fearture was added to PGM
*					-1 - if fearture already existed in PGM
*
****************************************************************************/
int PGMStruct::addFeature (std::vector <unsigned int>& domain, Feature* newFeature, const float& newWeight) {
	// Checking if newFeature is in the vector featureTypes
	unsigned int i, d, f;
	bool flagFeat, flagDomain;
	flagFeat = false;
	for (i = 0; i < featureTypes.size(); i++) {
		if (*newFeature == *featureTypes[i]) {
			f = i;
			flagFeat = true;
			break;
		}
	}
	if (!flagFeat) {
		// there is no feature in featureTypes equal to newFeature
		featureTypes.push_back(newFeature);
		f = featureTypes.size() - 1;
	} else {
		delete newFeature;
		newFeature = NULL;
	}
	// Checking if features contain 'domain'
	flagDomain = false;
	for (i = 0; i < features.size(); i++) {
		if (features[i].varsIDs == domain) {
			d = i;
			flagDomain = true;
			break;
		}
	}
	if (!flagDomain) {
		// there is no such domain in the PGM - adding a new one
		DomainFeatures domainFeats;
		domainFeats.varsIDs = domain;
		this->weights.push_back (newWeight);
		domainFeats.weightsIDs.push_back(this->weights.size() - 1);
		domainFeats.featsIDs.push_back(f);
		this->features.push_back(domainFeats);
	} else {
		// there is such domain in the PGM
		//  a new feature is added to the existing domain
		// counting how many features of the same type are in the domain
		int num = 0;
		int newFeatID = featureTypes[f]->getObjectID();
		for (i = 0; i < features[d].featsIDs.size(); i++) {
			if (f == features[d].featsIDs[i]) {
				std::cout << "\n\tWarning in PGMStruct::addFeature: PGM already contains an exactly the same feature.\n";
				return -1;
			}
			if (featureTypes[features[d].featsIDs[i]]->getObjectID() == newFeatID)
				num++;
		}
		// calculating max number of features of one type per this domain
		int maxNum = 1;
		for (i = 0; i < features[d].varsIDs.size(); i++)
			maxNum *= numOfValues[features[d].varsIDs[i]];
		// setting new weightID depending on values of num and maxNum
		if (num >= (maxNum - 1)) {
			features[d].weightsIDs.push_back(-1);
		} else {
			weights.push_back(newWeight);
			features[d].weightsIDs.push_back(weights.size() - 1);
		}
		// Adding a featureID to a vector of featuresIDs per this domain
		features[d].featsIDs.push_back(f);
	}
	return 0;
}


/****************************************************************************
*					PGMStruct::clear
*
*	Description:	Removes all variables in object internal variables
*	Parameters:		-
*	Returns:		-
*
****************************************************************************/
void PGMStruct::clear (void) {
	for (unsigned int i = 0; i < featureTypes.size(); i++)
		if (featureTypes[i] != NULL)
			delete (featureTypes[i]);
	nVars = 0;
	variables.clear();
	numOfValues.clear();
	featureTypes.size();
	weights.clear();
	features.clear();
}
